﻿#region Header

/*
Remote views based on linq
By

Pascal Hauser 
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

and

Raphael Gfeller
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

*/

#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Threading;
using Sebarf.Diagnostics.Interfaces;
using Sebarf.Services.Interfaces;
using Sebastof.Services.Interfaces;
using Utils;

#endregion

namespace Sebastof.Services.Impl {
	/// <summary>
	/// Description of the class
	/// </summary>
	[Serializable]
	public class MetadataLocatorRegisterService : Service, IMetadataLocatorService {
		public string Id { get; set; }

		#region Public Methods

		public MetadataLocatorRegisterService(string id) {
			Id = "MetadataLocatorRegisterService." + id;
		}

		public MetadataLocatorRegisterService() {
		}

		/// <summary>
		/// Get the metadata for the given object with the given key
		/// </summary>
		/// <typeparam name="T">type of the metadata</typeparam>
		/// <param name="key">object that the metadata applies</param>
		/// <param name="metaDataKey">key of the metadata</param>
		/// <returns></returns>
		public T GetMetadata<T>(object key, string metaDataKey) {
			Check.ArgumentNotNull(key, "key");
			Check.ArgumentNotNull(metaDataKey, "metaDataKey");
			Dictionary<string, object> data = GetMetaDataDictionary(key);
			GetLock().AcquireReaderLock(100);
			T toReturn = data.ContainsKey(metaDataKey) ? (T)data[metaDataKey] : default(T);
			;
			GetLock().ReleaseReaderLock();
			return toReturn;
		}

		/// <summary>
		/// Overrites the local metadata with the metadata available in the given <see cref="MetadataContext"/>
		/// </summary>
		/// <param name="context"></param>
		public void OverwriteMetadataWith(IMetadataLocatorService context) {
			//	context.GetLock().AcquireReaderLock(100);
			foreach (var entry in context.GetMetadataContext()) {
				SetMetadata(entry.Key, entry.Value);
			}
			//context.GetLock().ReleaseReaderLock();
		}

		/// <summary>
		/// Overrites the local metadata with the metadata available in the given <see cref="MetadataContext"/>
		/// </summary>
		/// <param name="context"></param>
		public void Add(IMetadataLocatorService service, object key) {
			foreach (var entry in service.GetMetadata(key)) {
				Add(key, entry.Key, entry.Value);
			}
		}

		/// <summary>
		/// Returns a list of all stored objects that contains metadata
		/// for that a dictionary of them metadarta
		/// </summary>
		/// <returns></returns>
		public Dictionary<object, Dictionary<string, object>> GetMetadataContext() {
			var toReturn = new Dictionary<object, Dictionary<string, object>>();
			foreach (var value in m_metaDataByObject) {
				toReturn.Add(m_usedObjects[value.Key].Target, value.Value);
			}
			return toReturn;
		}

		public void SetMetadata(object key, string metaDataKey, object value) {
			Add(key, metaDataKey, value);
		}

		public void Add(object key, string metaDataKey, object value) {
			Dictionary<string, object> data = GetMetaDataDictionary(key);
			GetLock().AcquireWriterLock(100);
			if (!data.ContainsKey(metaDataKey)) {
				data.Add(metaDataKey, value);
				RememberObject(key);
			}
			else {
				data[metaDataKey] = value;
			}
			GetLock().ReleaseWriterLock();
		}

		public IList<object> GetAllObjects() {
			var toReturn = new List<object>();
			GetLock().AcquireReaderLock(100);
			foreach (var entry in m_usedObjects) {
				if (entry.Value.IsAlive) {
					toReturn.Add(entry.Value.Target);
					continue;
				}
			}
			GetLock().ReleaseReaderLock();
			return toReturn;
		}

		/// <summary>
		/// Sets the metadata for a given object
		/// </summary>
		/// <param name="key">object that the metadata applies</param>
		/// <param name="metaData">new metadata</param>
		public void SetMetadata(object key, Dictionary<string, object> metaData) {
			Dictionary<string, object> data = GetMetaDataDictionary(key);
			GetLock().AcquireWriterLock(100);
			data.Clear();
			foreach (var entry in metaData) {
				data.Add(entry.Key, entry.Value);
				RememberObject(key);
			}
			GetLock().ReleaseWriterLock();
		}

		private void RememberObject(object key) {
			string id = GetId(key);
			if (id != string.Empty && !m_usedObjects.ContainsKey(id)) {
				m_usedObjects.Add(id, new WeakReference(key));
			}
		}

		#endregion

		#region Protected Methods

		protected override ServiceStatistics OnCollectStatisticData(ServiceStatistics statisticData) {
			GetLock().AcquireReaderLock(100);
			int objects = m_usedObjects.Count;
			GetLock().ReleaseReaderLock();
			statisticData.AddEntry("CountObjectsContaingMetaData", objects);
			return statisticData;
		}

		#endregion

		#region Private Methods

		private string GetId(object obj) {
			if ((obj is IIdentifiable)) {
				return (obj as IIdentifiable).Id;
			}
			return string.Empty;
		}

		private Dictionary<string, object> GetMetaDataDictionary(object obj) {
			// object provides its unique id
			if (obj == null || !(obj is IIdentifiable)) {
				return new Dictionary<string, object>();
			}
			string id = (obj as IIdentifiable).Id;

			GetLock().AcquireWriterLock(100);
			if (!m_metaDataByObject.ContainsKey(id)) {
				m_metaDataByObject.Add(id, new Dictionary<string, object>());
			}
			Dictionary<string, object> toReturn = m_metaDataByObject[id];
			GetLock().ReleaseWriterLock();

			return toReturn;
		}

		#endregion

		#region Private Fields

		private readonly Dictionary<string, Dictionary<string, object>> m_metaDataByObject =
			new Dictionary<string, Dictionary<string, object>>();

		/// <summary>
		/// TODO: clean up objects periodically
		/// </summary>
		private readonly Dictionary<string, WeakReference> m_usedObjects = new Dictionary<string, WeakReference>();

		[NonSerialized]
		private ReaderWriterLock m_lock = new ReaderWriterLock();

		public T GetMetadata<T>(object key, string metaDataKey, T defaultValue) {
			Dictionary<string, object> data = GetMetaDataDictionary(key);
			GetLock().AcquireReaderLock(100);
			if (!data.ContainsKey(metaDataKey)) {
				GetLock().ReleaseReaderLock();
				GetLock().AcquireWriterLock(100);
				data.Add(metaDataKey, defaultValue);
				GetLock().ReleaseWriterLock();
			}
			else {
				GetLock().ReleaseReaderLock();
			}
			GetLock().AcquireReaderLock(100);
			object toReturn = data[metaDataKey];
			GetLock().ReleaseReaderLock();
			return (T)toReturn;
		}

		public KeyValuePair<string, object>[] GetMetadata(object key) {
			Dictionary<string, object> data = GetMetaDataDictionary(key);

			return new List<KeyValuePair<string, object>>(data).ToArray();
		}

		private ReaderWriterLock GetLock() {
			if (m_lock == null) {
				m_lock = new ReaderWriterLock();
			}
			return m_lock;
		}

		#endregion

		#region IMetadataLocatorService Members

		public bool HasMetadata(object key) {
			return GetMetaDataDictionary(key).Count > 0;
		}

		public bool HasMetadata(object key, string metaDataKey) {
			Dictionary<string, object> data = GetMetaDataDictionary(key);
			return data.ContainsKey(metaDataKey);
		}

		#endregion
	}
}